/*
 * LinuxWrap.h
 *
 *  Created on: Mar 22, 2011
 *      Author: stud
 */
#ifndef LINUXWRAP_H_
#define LINUXWRAP_H_

#include <string>
#include <pthread.h>
#include <iostream>
#include <errno.h>
#include <cstring>
#include <semaphore.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <cstdlib>

//sched_get_priority_max(SCHED_RR) = 99
//sched_get_priority_min(SCHED_RR) = 1
#define PTHREAD_PRIORITY_LOWEST 		1
#define PTHREAD_PRIORITY_BELOW_NORMAL 	25
#define PTHREAD_PRIORITY_NORMAL 		50
#define PTHREAD_PRIORITY_ABOVE_NORMAL 	75
#define PTHREAD_PRIORITY_HIGHEST 		99
#define DWORD unsigned int
#define LONG long int

namespace linuxwrap
{
	inline void reportError(const char* txt)
	{
		std::cout << "ERROR: " << txt << " " << errno << ":" << strerror(errno) << std::endl;
	}
}

//====================================================
//CLASS : 	CountingSemaphore
//DESCR.: 	Encapsulates Counting Semaphore functionality
//====================================================
class CountingSemaphore
{
public:
	CountingSemaphore(unsigned init);
	~CountingSemaphore();
	void signal();
	void wait();
private:
    sem_t id;
};

//====================================================
//CLASS : 	Mutex
//DESCR.: 	Encapsulates Mutex functionality
//====================================================
class Mutex
{
public:
	Mutex();
	~Mutex();
	void signal();
	void wait();
private:
	pthread_mutex_t id;
};

//=====================================
// CLASS : Thread
// DESCR.: Wraps thread functionality
//=====================================
class Thread {
public:
	// Thread priorities
	enum ThreadPriority{
		PRIORITY_LOW          = PTHREAD_PRIORITY_LOWEST,
		PRIORITY_BELOW_NORMAL = PTHREAD_PRIORITY_BELOW_NORMAL,
		PRIORITY_NORMAL       = PTHREAD_PRIORITY_NORMAL,
		PRIORITY_ABOVE_NORMAL = PTHREAD_PRIORITY_ABOVE_NORMAL,
		PRIORITY_HIGH         = PTHREAD_PRIORITY_HIGHEST
	};
	Thread(std::string nme = "") : priority(PRIORITY_NORMAL), name(nme), rootMode(false) {}
	Thread(ThreadPriority pri, std::string nme = "");
	virtual ~Thread();
	void start();
	bool setPriority(ThreadPriority);
	ThreadPriority getPriority();
	std::string getName();
protected:
	virtual void run() = 0;
private:
	static void* threadMapper(void*);
	std::string name;
	pthread_t threadId;
	ThreadPriority priority;
	bool rootMode;
};

//====================================================
//CLASS : 	SleepTimer
//DESCR.:
//====================================================
class SleepTimer
{
public:
	SleepTimer() {}
	~SleepTimer() {}
	void sleep(DWORD ms)
	{
		usleep(ms*1000);
	}
};

//====================================================
//CLASS : 	ScopedLocker
//DESCR.:
//====================================================
class ScopedLocker
{
public:
	ScopedLocker(Mutex& m);
	~ScopedLocker();

private:
	Mutex& mutex;
};

//====================================================
//CLASS : 	MessageHandling
//DESCR.:
//====================================================
class MessageHandling {
public:
    template<typename Msg>
    static ssize_t sendMsg(int msgQId, const Msg& data, long int type = 1, int flag = 0)
    {
        MsgWrapper<Msg> msg;
        msg.type = type;
        msg.data = data;
        return msgsnd(msgQId, (void*) &msg, sizeof(data), flag);
    }
    template<typename Msg>
    static ssize_t receiveMsg(int msgQId, Msg& data, long int type = 0, int flag = 0)
    {
        MsgWrapper<Msg> msg;
        ssize_t recvSize = msgrcv(msgQId, (void*) &msg, sizeof(data), type, flag);
        data = msg.data;
        return recvSize;
    }
private:
    template<typename Msg>
    struct MsgWrapper {
    public:
        long int type;
        Msg data;
    };
};

//====================================================
//CLASS : 	Mailbox
//DESCR.:
//====================================================
template<typename Item>
class Mailbox
{
public:
	Mailbox(const LONG cap) : capacity(cap)
	{
		getSemaphore = new CountingSemaphore(0);    // Counts number of items in mailbox
		putSemaphore = new CountingSemaphore(cap);  // Counts number of free slots in mailbox
		key = msgget(IPC_PRIVATE, 0666 | IPC_CREAT); // Create message queue (IPC_PRIVATE is flag for new queue)
		if (key == -1)
		{
			linuxwrap::reportError("Mailbox constructor");
			exit(1);
		}
	}

	~Mailbox()
	{
		if (msgctl(key, IPC_RMID, NULL) == -1) // delete message queue
			linuxwrap::reportError("Mailbox destructor");
		delete putSemaphore;
		delete getSemaphore;
	}

	void put(const Item& n)
	{
		putSemaphore->wait();
		MessageHandling::sendMsg<Item>(key, n);
		getSemaphore->signal();
	}

	Item get()
	{
		Item *obj = new Item;
		getSemaphore->wait();
		MessageHandling::receiveMsg<Item>(key, *obj);
		putSemaphore->signal();
		return *obj;
	}

private:
	LONG capacity;
	CountingSemaphore* getSemaphore;
	CountingSemaphore* putSemaphore;
	key_t key;
};

#endif /* LINUXWRAP_H_ */
